home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 11 / CU Amiga Magazine's Super CD-ROM 11 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-06].iso / cucd / programming / oberonv4 / source / system / viewers.mod (.txt) < prev    next >
Oberon Text  |  1996-06-09  |  21KB  |  356 lines

  1. Syntax10.Scn.Fnt
  2. Syntax10b.Scn.Fnt
  3. ParcElems
  4. Alloc
  5. Syntax12.Scn.Fnt
  6. Syntax10i.Scn.Fnt
  7. MODULE Viewers; (*Copyright (c) ETH Z
  8. rich, 1990-96 / jg, rc 14.9.90*)
  9.     See, and update if necessary the history at the bottom of the file.
  10.     Implement Viewers on top of Display, i.e. of the screen representing the whole display area.
  11. Note: Display.FrameDesc defines the fields dsc, next and handle which are used by Viewers,but not by Display itself.
  12. IMPORT Display,Fonts;
  13. CONST
  14.     restore* = 0; modify* = 1; suspend* = 2; (*message ids*)
  15.     inf = MAX(INTEGER); (* "infinity" for frame and viewer sizes *)
  16. The viewers of a track are kept in a sorted, circularly linked list. The list will always contain a filler viewer with Y=0 and H=Display.Height. The first element (as defined by where the dsc field of a track points to) is the filler viewer. The following viewer are sorted so that a viewer with a larger Y follows one with a smaller Y, i.e. they are sorted from bottom to top.
  17. Sometimes there is a test, whther the next viewer has Y=0. This is used to test, whether it is the filler frame, as, by virtue of the sorting of the list and the fact, that viewers are always kept separate by at least minH one from another, no other frame except the first in the list may have an Y position of 0. Nevertheless it is very confusing for someone reading the code to see this test, instead of the more obvious test on state=filler.
  18. On the other hand, if you look at the initialisation part of this module you see, that FillerViewer.state is never initialised!
  19.     Viewer* = POINTER TO ViewerDesc;
  20.     ViewerDesc* = RECORD (Display.FrameDesc)
  21.         state*: INTEGER    (* state is read-only. No -, because Viewer was written before Oberon-2 *)
  22.     END;
  23.       (*state > 1: displayed
  24.         state = 1: filler    (* never used?? *)
  25.         state = 0: closed
  26.         state < 0: suspended*)
  27.         The used fields are:
  28.         restore: none.
  29.         modify: Y,H and state.
  30.         suspend: state.
  31.     ViewerMsg* = RECORD (Display.FrameMsg)
  32.         id*: INTEGER;
  33.         X*, Y*, W*, H*: INTEGER;
  34.         state*: INTEGER
  35.     END;
  36.     Track = POINTER TO TrackDesc;
  37.     TrackDesc = RECORD (ViewerDesc)
  38.         under: Display.Frame (* points to tracks covered by this track. *)
  39.     END;
  40.         curW is the width of the already configured part, and is initilaized to 0.
  41.         minH is the minimum vertical distance of two viewers. This is used in Open.
  42.         DW and DH duplicate Display.Width and Display.Height.
  43.         FillerTrack is the track covering all unused display area.
  44.         FillerViewer is the viewer covering all unused track area.
  45.         buf stores the last closed viewer.
  46.     curW*, minH*, DW, DH: INTEGER;
  47.     FillerTrack: Track;
  48.     FillerViewer: Viewer; 
  49.     buf: Viewer;
  50. PROCEDURE Open*(V: Viewer; X, Y: INTEGER);
  51. Opens a viewer if the state of the passed viewer is =closed and the X coordinate is not "infinity".
  52.     VAR T, u, v: Display.Frame; M: ViewerMsg;
  53.    BEGIN
  54.     IF (V.state = 0) & (X < inf) THEN
  55.             Truncate Y to Display.Height.
  56.         IF Y > DH THEN Y := DH END;
  57.             Search the track containing X.
  58.         T := FillerTrack.next;
  59.         WHILE X >= T.X + T.W DO T := T.next END;
  60.             Search the viewer in the track list, which is just below Y.
  61.             v:=viewer containing Y.
  62.             u:=viewer just below v.
  63.         u := T.dsc; v := u.next;
  64.         WHILE Y > v.Y + v.H DO u := v; v := u.next END;
  65.             Adjust the requested Y, so that the new viewer is at least minH height.
  66.         IF Y < v.Y + minH THEN Y := v.Y + minH END;
  67. If the next viewer is not the filler viewer and the requested Y position is within the first minH pixel of the viewer then open the new viewer in place of the old one.
  68. Otherwise reduce the viewer containing Y to terminate at position Y, where the new viewer is opened.
  69.         IF (v.next.Y # 0) & (Y > v.Y + v.H - minH) THEN
  70.             WITH v: Viewer DO
  71.                 (*
  72.                     The new viewer is openend in place of the old one.
  73.                 *)
  74.                 V.X := T.X; V.W := T.W; V.Y := v.Y; V.H := v.H;
  75.                 (*
  76.                     The old viewer is sent a suspend message and unlinked.
  77.                     The new viewer becomes state=displayed.
  78.                 *)
  79.                 M.id := suspend; M.state := 0;
  80.                 v.handle(v, M); v.state := 0; buf := v;
  81.                 V.next := v.next; u.next := V;
  82.                 V.state := 2
  83.             END
  84.         ELSE
  85.             (*
  86.                 The new viewer is opened within the old one, and covers the
  87.                 area between Y and the lower bound of the old one.
  88.             *)
  89.             V.X := T.X; V.W := T.W; V.Y := v.Y; V.H := Y - v.Y;
  90.             (*
  91.                 The old viewer is reduced to cover only the area between its
  92.                 top boundary and Y, and receives a modify message.
  93.             *)
  94.             M.id := modify; M.Y := Y; M.H := v.Y + v.H - Y;
  95.             v.handle(v, M); v.Y := M.Y; v.H := M.H;
  96.             (*
  97.                 The new viewer is linked into the list and gets state=displayed.
  98.             *)
  99.             V.next := v; u.next := V;
  100.             V.state := 2
  101.         END
  102.    END Open;
  103. PROCEDURE Change*(V: Viewer; Y: INTEGER);
  104. Reduce the viewer by moving the lower edge to the new Y value. This procedure cannot be used to increase the viewer, i.e. move the lower edge towards the bottom of the screen.
  105.     v: Display.Frame; M: ViewerMsg;
  106. BEGIN
  107.     IF V.state > 1 THEN
  108.             truncate Y against Display.Height.
  109.         IF Y > DH THEN Y := DH END;
  110.             If the next next viewer is not the filler viewer then make sure, 
  111.             that the new Y value is at least minH below the top edge fo the
  112.             next window.
  113.         v := V.next;
  114.         IF (v.next.Y # 0) & (Y > v.Y + v.H - minH) THEN
  115.             Y := v.Y + v.H - minH
  116.         END;
  117.             Modify only, if the new position is at least minH above the current position.
  118.         IF Y >= V.Y + minH THEN
  119.             M.id := modify; M.Y := Y; M.H := v.Y + v.H - Y;
  120.             v.handle(v, M); v.Y := M.Y; v.H := M.H;
  121.             V.H := Y - V.Y
  122.         END
  123. END Change;
  124. PROCEDURE RestoreTrack(S: Display.Frame);
  125.     Remove track S and restore all tracks currently covered by S.
  126.     T, t, v: Display.Frame; M: ViewerMsg;
  127. BEGIN
  128.     WITH S: Track DO
  129.             Search a (the?) track, preceeding one with the same X position as this one.
  130.             Is this really a search for the predecessor of S? Then why not test for t.next=S?
  131.         t := S.next;
  132.         WHILE t.next.X # S.X DO t := t.next END;
  133. Get the list of tracks covered by this one, and locate the last track in this list. This seems to indicate, that the list of covered tracks is no more a circular one.
  134.         T := S.under;
  135.         WHILE T.next # NIL DO T := T.next END;
  136. All tracks in the S.under list are inserted between the track preceeding S and the track  following S. This operation removes S from the track list. 
  137.         t.next := S.under; T.next := S.next;
  138. A restore message is sent to all viewers of the newly uncovered tracks. Then the state of the viewer is chagend from suspended to displayed.
  139.         M.id := restore;
  140.         REPEAT
  141.             t := t.next;
  142.             v := t.dsc;
  143.             REPEAT
  144.                 v := v.next; v.handle(v, M);
  145.                 WITH v: Viewer DO v.state := - v.state END
  146.             UNTIL v = t.dsc
  147.         UNTIL t = T
  148. END RestoreTrack;
  149. PROCEDURE Close*(V: Viewer);
  150.     Closes a viewer, if it has state=displayed.
  151.     T, U: Display.Frame; M: ViewerMsg;
  152. BEGIN
  153.     IF V.state > 1 THEN
  154.         U := V.next;
  155.             Search the track which contains this viewer.
  156.         T := FillerTrack;
  157.         REPEAT T := T.next UNTIL V.X < T.X + T.W;
  158. If the viewer is not the only one in this track, or  the track does not cover any others, then only the viewer is closed. Thus the track will survive the closure of the last window on it, if it doesn't cover any other tracks.
  159.         IF (T(Track).under = NIL) OR (U.next # V) THEN
  160.             (*
  161. Send a suspend message to the viewer to be closed, then set it's state to closed and assigned it to the last closed viewer buffer.
  162.             *)
  163.             M.id := suspend; M.state := 0;
  164.             V.handle(V, M);
  165.             V.state := 0; buf := V;
  166.             (*
  167. Send a modify message to the viewer above the closed one. Then adjust its Y and H fields.
  168.             *)
  169.             M.id := modify; M.Y := V.Y; M.H := V.H + U.H;
  170.             U.handle(U, M); U.Y := M.Y; U.H := M.H;
  171.             (*
  172. Search the viewer preceeding the closed one, and unlink the closed one from the viewer list.
  173.             *)
  174.             WHILE U.next # V DO U := U.next END;
  175.             U.next := V.next
  176.         ELSE (*close track*)
  177.             (*
  178. Send a suspend message to the closed viewer and to the filler viewer of this track. Remeber the closed viewer in the buffer and use RestoreTrack to close the track and make all covered tracks visible.
  179.             *)
  180.             M.id := suspend; M.state := 0;
  181.             V.handle(V, M);
  182.             V.state := 0; buf := V;
  183.             U.handle(U, M); U(Viewer).state := 0;
  184.             RestoreTrack(T);
  185.         END
  186. END Close;
  187. PROCEDURE Recall*( VAR V: Viewer);
  188.     Returns the last closed viewer.
  189. BEGIN
  190.     V := buf
  191. END Recall;
  192. PROCEDURE This*(X, Y: INTEGER): Viewer;
  193.     Return the viewer which contains point X,Y.
  194.     T, V: Display.Frame;
  195. BEGIN
  196.     IF (X < inf) & (Y < DH) THEN
  197.             Search the track, which contains the X component of the point. There is no test for
  198.             the end of the list, because all area is covered either by a "real" track, or the filler track.
  199.             Also, the search assumes, that the list is ordered, from left to right.
  200.         T := FillerTrack;
  201.         REPEAT T := T.next UNTIL X < T.X + T.W;
  202.             Search the viewer containing the Y component of the point. There is no test for
  203.             the end of the list, because all area is covered either by a "real" viewer, or the filler viewer.
  204.             Also, the search assumes, that the list is ordered, from low to high.
  205.         V := T.dsc;
  206.         REPEAT V := V.next UNTIL Y < V.Y + V.H;
  207.         RETURN V(Viewer)
  208.     ELSE
  209.         RETURN NIL
  210. END This;
  211. PROCEDURE Next* (V: Viewer): Viewer;
  212.     Return the next viewer in the track. I don't really know, why this procedure is there,
  213.     as the next field is exported.
  214. BEGIN
  215.     RETURN V.next(Viewer);
  216. END Next;
  217. PROCEDURE Locate*(X, H: INTEGER; VAR fil, bot, alt, max: Display.Frame);
  218. This procedure examines the viewers in the track containing X and returns this four viewers:
  219. fil: filler viewer of the track.
  220. bot: bottom viewer of this track.
  221. alt: The first viewer (from bottom) with a height of at least H.
  222. max: the viewer which has the greates height.
  223. If the track doesn't contain any viewers, than bot, alt and max are equal to the filler viewer. If there is exactly one viewer, than alt will always return it.
  224. If there are at least two viewers, alt will start the search with the second one. Thus it will never return the first (most bottom) viewer, even if it larger or equal to H. If none of the searched viewers is larger than H, then the largest one is returned.
  225.     T, V: Display.Frame;
  226. BEGIN
  227.     IF X < inf THEN
  228.             Locate the track containing X.
  229.         T := FillerTrack;
  230.         REPEAT T := T.next UNTIL X < T.X + T.W;
  231.             Get the filler and the bottom viewer.
  232.         fil := T.dsc;
  233.         bot := fil.next;
  234.             If there is more than one viewer, then search (starting with the second viewer) for
  235.             the first one with height>=H. If non found, return the one with the greates height.
  236.             If there is less then two viewers, return the one found, or the filler viewer. 
  237.         IF bot.next # fil THEN
  238.             alt := bot.next;
  239.             V := alt.next;
  240.             WHILE (V # fil) & (alt.H < H) DO
  241.                 IF V.H > alt.H THEN alt := V END;
  242.                 V := V.next
  243.             END
  244.         ELSE
  245.             alt := bot
  246.         END;
  247.             Set max to the viewer with the greatest height.
  248.         max := T.dsc;
  249.         V := max.next;
  250.         WHILE V # fil DO
  251.             IF V.H > max.H THEN max := V END;
  252.             V := V.next
  253.         END
  254. END Locate;
  255. PROCEDURE InitTrack*(W, H: INTEGER; Filler: Viewer);
  256.     Create a new track with specified width and height.
  257.     S: Display.Frame;
  258.     T: Track;
  259. BEGIN
  260.     IF Filler.state = 0 THEN
  261.             Prepare the filler viewer for the track, by setting the correct bounding box, state and performing
  262.             the linking for the circular list.
  263.         Filler.X := curW; Filler.W := W;
  264.         Filler.Y := 0; Filler.H := H;
  265.         Filler.state := 1; (* = filler *)
  266.         Filler.next := Filler;
  267.         (* Filler.dsc is not initialized. *)
  268.             Create and initialize the new track.
  269.         NEW(T);
  270.         T.X := curW; T.W := W; T.Y := 0; T.H := H;
  271.         T.dsc := Filler; T.under := NIL;
  272.         (* FillerTrack.state is not initialized. *)
  273. The filler track, and its filler viewer are reduced to occupy only the remaining room at the right of this track.
  274.         FillerViewer.X := curW + W; FillerViewer.W := inf - FillerViewer.X;
  275.         FillerTrack.X := FillerViewer.X; FillerTrack.W := FillerViewer.W;
  276. Search for the predecessor of the filler track, and add the new track to the track list. Adjust curW.
  277.         S := FillerTrack;
  278.         WHILE S.next # FillerTrack DO S := S.next END;
  279.         S.next := T;
  280.         T.next := FillerTrack;
  281.         curW := curW + W
  282. END InitTrack;
  283. PROCEDURE OpenTrack*(X, W: INTEGER; Filler: Viewer);
  284. Open a new track at position X with width W over existing tracks. X and W are "proposals". X is adjusted to the left, so that it fully covers the track within which the original X lied. W is adjusted, so that the new track fully covers the track on which X+W lied.
  285.     newT: Track; S, T, t, v: Display.Frame; M: ViewerMsg;
  286. BEGIN
  287.     IF (X < inf) & (Filler.state = 0) THEN
  288. Search for the rightmost track (S) which is completely at the left of X and for the track (T) which contains the position X+W.
  289.         S := FillerTrack; T := S.next;
  290.         WHILE X >= T.X + T.W DO S := T; T := S.next END;
  291.         WHILE X + W > T.X + T.W DO T := T.next END;
  292. Send a suspend message to all viewers which are (partially) covered by the new track.
  293. Note: It seems, that filler viewers may be suspended too.
  294.         M.id := suspend;
  295.         t := S;
  296.         REPEAT
  297.             t := t.next; v := t.dsc;
  298.             REPEAT v := v.next;
  299.                 WITH v: Viewer DO
  300.                     M.state := -v.state; v.handle(v, M); v.state := M.state
  301.                 END
  302.             UNTIL v = t.dsc
  303.         UNTIL t = T;
  304.             Prepare the list of viewers, as list containing only the filler viewer.
  305.         Filler.X := S.next.X; Filler.W := T.X + T.W - S.next.X; Filler.Y := 0; Filler.H := DH;
  306.         Filler.state := 1;
  307.         Filler.next := Filler;
  308. Create the new track. Link in the viewer list. Move the list of covered track under this track, and link in this track into the track list.
  309.         NEW(newT);
  310.         newT.X := Filler.X; newT.W := Filler.W; newT.Y := 0; newT.H := DH;
  311.         newT.dsc := Filler; newT.under := S.next; S.next := newT;
  312.         newT.next := T.next; T.next := NIL
  313. END OpenTrack;
  314.    PROCEDURE CloseTrack*(X: INTEGER);
  315.     VAR T, V: Display.Frame; M: ViewerMsg;
  316.    BEGIN
  317.     IF X < inf THEN
  318.         T := FillerTrack;
  319.         REPEAT T := T.next UNTIL X < T.X + T.W;
  320.         IF T(Track).under # NIL THEN
  321.             M.id := suspend; M.state := 0; V := T.dsc;
  322.             REPEAT V := V.next; V.handle(V, M); V(Viewer).state := 0 UNTIL V = T.dsc;
  323.             RestoreTrack(T)
  324.         END
  325.     END
  326.    END CloseTrack;
  327. PROCEDURE Broadcast*(VAR M: Display.FrameMsg);
  328.     Call the handler of each viewer in each track, and pass it the message M, and the viewer itself.
  329.     T, V: Display.Frame;
  330. BEGIN
  331.     T := FillerTrack.next;
  332.     WHILE T # FillerTrack DO
  333.         V := T.dsc; 
  334.         REPEAT V := V.next; V.handle(V, M) UNTIL V = T.dsc;
  335.         T := T.next
  336.     END;
  337.     Display.Synchronize;
  338. END Broadcast;
  339. BEGIN
  340.     buf := NIL;
  341.     DW := Display.Width; DH := Display.Height; (* copy for "easier" access. *)
  342.     curW := 0; minH := Fonts.Default.height + 4;
  343.     Create two circular linked lists, one containing the only viewer FillerViewer, and the other containing the
  344.     only track FillerTrack.
  345.     NOTE: The filler viewer has no handler !
  346.     NEW(FillerViewer);
  347.     FillerViewer.X := 0; FillerViewer.W := inf; FillerViewer.Y := 0; FillerViewer.H := DH;
  348.     FillerViewer.next := FillerViewer; (* FillerViewer.dsc and FillerViewer.state are not initialized. *)
  349.     NEW(FillerTrack);
  350.     FillerTrack.X := 0; FillerTrack.W := inf; FillerTrack.Y := 0; FillerTrack.H := DH;
  351.     FillerTrack.dsc := FillerViewer; (* A filler viewer is always present in every track. *)
  352.     FillerTrack.next := FillerTrack; (* FillerTrack.under and FillerTrack.state are not initialized. *)
  353. END Viewers.
  354. Date    Author    Modification
  355. 1996-06-02    claudio@dial.eunet.ch    Created first unified version.
  356.